use cmsis_rtos2_rs::os_delay;
use driver_utils::service::motor::Dir;
use driver_utils::service::motor::Motor;
use driver_utils::service::motor::MotorDrv;
use periph_utils::AdcInit;
use periph_utils::DmaConfig;
use periph_utils::GpioConfig;
use periph_utils::stm32f1;
use periph_utils::PeriphReg;
use utils::UninitCell;

use crate::cp_heat::HeatErr;

use super::sensor;


#[derive(GpioConfig, AdcInit, DmaConfig)]
#[gpio(init = "gpio_init",
    io(name = "analog", io = "PC2", mode = "AIN"),
    io(name = "dir", io = "PA0", mode = "OUT_PP", set, reset),
    io(name = "stir_cup", io = "PA5", mode = "OUT_PP", set, reset),
    io(name = "v5", io = "PA6", mode = "OUT_PP", set, reset),
    io(name = "stir", io = "PA7", mode = "OUT_PP", set, reset),
    io(name = "clamp", io = "PA8", mode = "OUT_PP", set, reset),
)]
#[adc_config(adc = 1, chs = [12])]
#[dma(
    init = "dma_init",
    ch(name = "adc", dma = 1, ch = 1, size = 16, dir = "read", circ)
)]
struct Init;

#[derive(PeriphReg)]
#[periph(periph = "ADC1")]
struct ADC;

static ADC_DATA_BUF: UninitCell<[u16; 1]> = UninitCell::const_new();

pub unsafe fn init() {
    Init::gpio_init();
    Init::dma_init();
    Init::adc_init();
    Init::adc_dma1_ch1_read(
        ADC_DATA_BUF.as_ptr() as _,
        ADC.dr.as_ptr() as _,
        ADC_DATA_BUF.len() as _,
    );
    Init::adc_start();
}

pub fn analog_value() -> u16 {
    ADC_DATA_BUF[0]
}

pub fn stir_open() {
    Init::dir_reset();
    Init::stir_set();
}

pub fn stir_close() {
    Init::dir_reset();
    Init::stir_reset();
}

pub fn stir_cap_open() -> Result<(), HeatErr> {
    type M = Motor<StirCupMotorCtrl>;
    M::run_to_ccw_origin()
}

pub fn stir_cap_close() -> Result<(), HeatErr> {
    type M = Motor<StirCupMotorCtrl>;
    M::run_to_cw_origin()
}

struct StirCupMotorCtrl;

impl MotorDrv for StirCupMotorCtrl {

    type Error = HeatErr;
    const ALM_ERR: Self::Error = HeatErr::StirCapAlm;
    const TIMEOUT_ERR: Self::Error = HeatErr::StirCapTimeout;

    fn analog_value() -> u16 {
        analog_value()
    }

    fn delay(tick: u32) {
        os_delay(tick);
    }

    fn is_ccw_origin() -> bool {
        sensor::is_stir_top()
    }

    fn is_cw_origin() -> bool {
        sensor::is_stir_bottom()
    }

    fn set_dir(dir: Dir) {
        match dir {
            Dir::CCW => {
                Init::dir_set();
                Init::stir_cup_set();
            },
            Dir::CW => {
                Init::dir_reset();
                Init::stir_cup_set();
            },
            Dir::Brake => {
                Init::dir_reset();
                Init::stir_cup_reset();
            }
        }
    }
    const ALM_VALUE: u16 = 2500;
}

